<?php
    class WZ_Activerecord
    {
        private $_config;
        private $_db;
        private $_model;

		public function __construct($model) 
		{
            $config = array(
                'db' => \WZ::get('ZendDb'),
                'name' => $model->TableName()
            );
            $this->_config = $config;
            $this->_db = new \Zend_Db_Table($config);
            $this->_model = get_class($model);
            return $this;
		}
        
        public function info()
        {
            return $this->_db->info();
        }
        
        public function find($id)
        {
            $res = $this->_db->find($id)->toArray();
            foreach ($res as $row) {
                return self::toObject($row);
            }
            return null;
        }
        
        public function findBy($column, $value, $one = false)
        {
            $column = \WZ_Inflector::uncamelize(lcfirst($column));
            $column = \WZ_Inflector::lower($column);
            if ($column == 'id') {
                $primary = $this->getPrimary();
                if ($primary != 'id') {
                    $column = $primary;
                }
            }

            $result = $this->select(array(), "$column = '$value'");
            if (null !== $result && true === $one) {
                if (is_array($result)) {
                    return current($result);
                } else {
                    return $result;
                }
            }
            return $result;
        }
        
        public function toObject($data)
        {
            $object = new $this->_model;
            foreach ($data as $key => $value) {
                $setter = 'set' . ucfirst(\WZ_Inflector::camelize($key));
                $object->$setter($value);
            }
            return $object;
        }
        
        public function fetchAll($where = null) 
        {
            $res = $this->_db->fetchAll($where);
            $collection = array();
            if (count($res)) {
                foreach ($res as $row) {
                    $collection[] = $this->toObject($row);
                }
            }
            return $collection;
        }
        
        public function fetchRow($where = null) 
        {
            $res = $this->_db->fetchAll($where);
            if (count($res)) {
                foreach ($res as $row) {
                    return $this->toObject($row);
                    break;
                }
            }
            return null;
        }
        
        public function getPrimary()
        {
            $info = $this->info();
            return $info['primary'][1];
        }
        
        public function getAdapter()
        {
            return $this->_db->getAdapter();
        }
        
        public function query($query, $object = true)
        {
            $db = $this->getAdapter();
            if (strstr(\WZ_Inflector::lower($query), 'select ')) {
                $stmt = $db->prepare($query);
                $stmt->execute(); 
                $res = $stmt->fetchAll();
                $collection = array();
                if (count($res)) {
                    if (false === $object) {
                        if (count($res) == 1) {
                            $res = current($res);
                        }
                        return $res;
                    }
                    foreach ($res as $row) {
                        $collection[] = $this->toObject($row);
                    }
                }
                if (count($collection) == 1) {
                    $collection = current($collection);
                }
                return $collection;
            } else {
                if (strstr(\WZ_Inflector::lower($query), 'insert into ')) {
                    $stmt = $db->prepare($query);
                    $stmt->execute(); 
                    return $db->lastInsertId();
                } else {
                    return $db->query($query)->rowCount();
                }
            }
        }
        
        public function getTotal()
        {
            return count($this->select());
        }
        
        public function select($fields = array(), $where = null)
        {
            $args = func_get_args();
            if (count($args) == 2) {
                $fields = $args[0];
                $where = $args[1];
            } elseif (count($args) == 1) {
                if (is_array($args[0])) {
                    $fields = $args[0];
                    $where = null;
                } elseif (is_numeric($args[0]) || is_string($args[0])) {
                    $where = $args[0];
                    $fields = array();
                } else {
                    $where = null;
                    $fields = array();
                }
            } else {
                $where = null;
                $fields = array();
            }
            if (!count($fields)) {
                $fields = '*';
            } else {
                $fields = implode(', ', $fields);
            }
            if (null === $where) {
                $where = "1 = 1";
            } else if (is_numeric($where)) {
                $primary = $this->getPrimary();
                $where = "$primary = '$where'";
            }
            $table = $this->_config['name'];
            $q = "SELECT $fields FROM $table WHERE $where";
            $res = $this->query($q);
            if (is_object($res)) {
                return $res;
            }
            if (count($res) == 0) {
               return null;
            } elseif (count($res) == 1) {
                return $res[0];
            } else {
                return $res;
            }
        }
        
        public function insert($data)
        {
            $execute = true;
            $notNull = false;
            $table = $this->_config['name'];
            $q = "INSERT INTO $table SET ";
            if (is_array($data)) {
                if (count($data)) {
                    foreach ($data as $key => $value) {
                        if (null !== $value) {
                            $q .= "$key = '" . addslashes($value) . "', ";
                            $notNull = true;
                        }
                    }
                } else {
                    $execute = false;
                }
            } else if (is_object($data)) {
                $vars = get_object_vars($data);
                if (count($vars)) {
                    foreach ($vars as $key => $value) {
                        if (null !== $value) {
                            $q .= "$key = '" . addslashes($value) . "', ";
                            $notNull = true;
                        }
                    }
                } else {
                    $execute = false;
                }
            }
            if (true === $execute && true === $notNull) {
                $q = substr($q, 0, -2);
                return $this->query($q);
            }
            return false;
        }
        
        public function update($data, $where = null)
        {
            $execute = true;
            $notNull = false;
            $table = $this->_config['name'];
            $q = "UPDATE $table SET ";
            if (is_array($data)) {
                if (count($data)) {
                    foreach ($data as $key => $value) {
                        if (null !== $value) {
                            $q .= "$key = '" . addslashes($value) . "', ";
                            $notNull = true;
                        }
                    }
                } else {
                    $execute = false;
                }
            } else if (is_object($data)) {
                $vars = get_object_vars($data);
                if (count($vars)) {
                    foreach ($vars as $key => $value) {
                        if (null !== $value) {
                            $q .= "$key = '" . addslashes($value) . "', ";
                            $notNull = true;
                        }
                    }
                } else {
                    $execute = false;
                }
            }
            if (true === $execute && true === $notNull) {
                if (null === $where) {
                    $where = "1 = 1";
                } else if (is_numeric($where)) {
                    $primary = $this->getPrimary();
                    $where = "$primary = '$where'";
                }
                $q = substr($q, 0, -2) . " WHERE $where";
                return $this->query($q);
            }
            return false;
        }
        
        public static function delete($where)
        {
            if (null === $where) {
                $where = "1 = 1";
            } else if (is_numeric($where)) {
                $primary = $this->getPrimary();
                $where = "$primary = '$where'";
            }
            $table = $this->_config['name'];
            $q = "DELETE FROM $table WHERE $where";
            return $this->query($q);
        }
        
        public static function newObj()
        {
            $obj = new \WZ_DbObject;
            $obj->WZDbTable = $this->_config['name'];
            return $obj;
        }
        
        public function __call($name, $args)
        {
            if (strncmp($name, 'findBy', 6) === 0) {
                return $this->findBy(substr($name, 6), reset($args));
            }
            elseif (strncmp($name, 'findOneBy', 9) === 0) {
                return $this->findBy(substr($name, 9), reset($args), true);
            }
            throw new \BadMethodCallException('Method "' . \WZ_Inflector::clean($name) . '" does not exist.');
        }
    }